<?php


namespace seecms\lib\provider;

use seecms\db\SeeDbException;
use seecms\See;
use seecms\lib\Auth;
use seecms\lib\Operate;
use seecms\lib\UserPermission;
use seecms\SeeConst;
use seecms\SeeLang;
use magein\utils\Result;
use magein\utils\Tree;
use Psr\Cache\InvalidArgumentException;

class User
{

    /**
     * @return bool
     */
    public function isRoot(): bool
    {
        return $this->uuid() === 1;
    }

    /**
     * @return int
     */
    public function uuid(): int
    {
        return intval(self::info()['id'] ?? 0);
    }

    /**
     *  设置用户心跳数据
     * @return void
     */
    public function setHeart()
    {
        try {
            $token = $_COOKIE['token'] ?? '';
            if ($token) {
                See::cache()->set('user_login_heart_' . self::uuid(), [
                    'token' => $token,
                    'expire_time' => time() + 300,
                ], 86400);
            }
        } catch (InvalidArgumentException $exception) {

        }
    }

    /**
     * 设置或者清空登录信息
     * @param $data
     * @return array|bool
     */
    public function info($data = null)
    {
        $token = $_COOKIE['token'] ?? '';
        if (empty($token)) {
            return [];
        }

        if (empty($data)) {
            return See::cache()->get($token, []) ?: [];
        }

        try {
            See::cache()->set($token, $data);
        } catch (InvalidArgumentException $exception) {

        }

        return true;
    }

    /**
     * 获取用户权限,菜单,权限规则,角色等
     * @param string|null $type
     * @return array|mixed|null
     */
    public function permission(string $type = null)
    {
        $permission = UserPermission::init($this->uuid());
        $record = $permission->get();
        if ($type !== null) {
            return $record ? $record[$type] : [];
        }
        return $record;
    }

    /**
     * 清除权限缓存
     * @param $type
     * @return bool
     */
    public function clearCachePermission($type): bool
    {
        try {
            if ($type === 'all') {
                foreach (['role', 'menu', 'rule'] as $item) {
                    $name = sprintf('current_user_%s_list_%s', $item, $this->uuid());
                    See::cache()->delete($name);
                }
            } else {
                $name = sprintf('current_user_%s_list_%s', $type, $this->uuid());
                See::cache()->delete($name);
            }
        } catch (\Psr\SimpleCache\InvalidArgumentException $e) {

        }

        return true;
    }


    /**
     * 用户菜单
     * @return array
     * @throws InvalidArgumentException
     * @throws SeeDbException
     */
    public function menu(): array
    {
        $key = 'current_user_menu_list_' . $this->uuid();

        // 非debug模式下从缓存中获取
        if (!See::config()->debug()) {
            $cache_menu = See::cache()->get($key);
            if ($cache_menu) {
                return $cache_menu;
            }
        }

        if (Auth::user()->isRoot()) {
            $menus = See::table()->menu->where('status', SeeConst::STATUS_OPEN)->order('sort asc')->select();
        } else {
            $menu_ids = $this->permission(__FUNCTION__) ?: [];
            $menus = See::table()->menu->where('id', 'in', $menu_ids)->order('sort asc')->select();
        }

        $tree = Tree::init($menus);
        $tree->setParentId('parent_id');
        $tree->setChild('childlist');
        $console_menu = SeeLang::instance()->get('console_menu');
        $data = $tree->transfer(function ($item) use ($console_menu) {
            $title = $item['title'];
            $url = $item['url'] ?? '';
            if ($console_menu) {
                $_title = $console_menu[$url] ?? '';
                $title = $_title ?: $title;
            }
            $item['title'] = $title;
            if ($url) {
                if (preg_match('/^http/', $url)) {
                    $item['href'] = $url;
                } else {
                    $item['href'] = auth_route($url);
                }
            } else {
                $item['href'] = '';
            }

            $item['openType'] = $item['open_type'] ?? '';
            return $item;
        });

        See::cache()->set($key, $data);

        return $data ?: [];
    }


    /**
     * 用户授权的规则
     * @return array
     */
    public function rule(): array
    {
        // 从缓存中获取
        $key = 'current_user_rule_list_' . $this->uuid();
        $data = See::cache()->get($key);
        if ($data) {
            return $data;
        }

        $rule_ids = $this->permission(__FUNCTION__) ?: [];

        try {

            // 获取用户组的权限
            $role = $this->role();
            if ($role) {
                $role_ids = array_keys($role);

                $role_rule = See::table()->user_role->where('id', 'in', $role_ids)->column('rule');

                foreach ($role_rule as $item) {
                    $item = explode(',', $item);
                    $rule_ids = array_merge($rule_ids, $item);
                }
            }

            $rule_ids = array_unique($rule_ids);

            // 用户的权限规则
            $data = See::table()->user_rule->where('id', 'in', $rule_ids)->column('url');
            if ($data) {
                See::cache()->set($key, $data);
            }
        } catch (SeeDbException|InvalidArgumentException $exception) {
            $data = [];
        }

        return $data ?: [];
    }

    /**
     * 用户角色
     * @return array
     */
    public function role(): array
    {
        $key = 'current_user_role_list_' . $this->uuid();

        // 从缓存中获取
        $cache_role = See::cache()->get($key);
        if ($cache_role) {
            return $cache_role;
        }

        $roles = $this->permission(__FUNCTION__);

        try {
            $data = See::table()->user_role->where('id', 'in', $roles)->column('name', 'id');

            if ($data) {
                See::cache()->set($key, $data);
            }

        } catch (SeeDbException|InvalidArgumentException $exception) {
            $data = [];
        }

        return $data ?: [];
    }

    /**
     * 设置密码
     * @return Result
     * @throws SeeDbException
     */
    public function setPassword(): Result
    {
        $old = See::request()->post('old');
        $new = See::request()->post('new');
        $confirm = See::request()->post('confirm');

        if (empty($old)) {
            return Result::error(SeeLang::user('enter_old_passwd'));
        }

        if (empty($new)) {
            return Result::error(SeeLang::user('old_passwd_error'));
        }

        $len = strlen($new);
        if ($len < 6 || $len > 18) {
            return Result::error(SeeLang::user('enter_new_passwd_format'));
        }

        if (empty($confirm)) {
            return Result::error(SeeLang::user('enter_confirm_passwd'));
        }

        if ($new != $confirm) {
            return Result::error(SeeLang::user('new_not_equal_confirm'));
        }

        $user = See::table()->user->find(Auth::user()->uuid());
        if (empty($user)) {
            return Result::error(SeeLang::user('not_found'));
        }

        if (!Auth::checkPassword($old, $user['password'], $user['encrypt'])) {
            return Result::error(SeeLang::user('old_passwd_error'));
        }

        See::table()->user->where('id', Auth::user()->uuid())
            ->data('password', $new)
            ->update();

        return Result::success(null, SeeLang::user('reset_passwd_success'));
    }

    /**
     * 效验密码
     * @return Result
     * @throws InvalidArgumentException
     */
    public function verifyPassword(): Result
    {
        $password = See::request()->post('password');

        if (empty($password)) {
            return Result::error(SeeLang::user('enter_passwd'));
        }

        $info = Auth::user()->info();
        if (!Auth::checkPassword($password, $info['password'] ?? '', $info['encrypt'] ?? '')) {
            return Result::error(SeeLang::user('passwd_error'));
        }

        // 记录此次验证信息
        $name = md5($info['id'] . '');
        $token = md5(uniqid());
        See::cache()->set($name, $token, 600);

        return Result::success($token);
    }


    /**
     * 设置头像
     * @return Result
     * @throws SeeDbException
     */
    public function setAvatar(): Result
    {
        $upload = new Upload();
        $filepath = $upload->move();

        See::table()->user->where('id', Auth::user()->uuid())
            ->data('avatar', $filepath)
            ->update();

        $data = Auth::user()->info();
        $data['avatar'] = $filepath;
        Auth::user()->info($data);

        return Result::success([
            'url' => $filepath
        ]);
    }


    /**
     * 更新
     * @return Result
     * @throws SeeDbException
     */
    public function updateInfo(): Result
    {
        $data = [];
        $data['token'] = See::request()->post('token');
        $data['email'] = See::request()->post('email');
        $data['nickname'] = See::request()->post('nickname');
        $data['phone'] = See::request()->post('phone');
        $data = array_filter($data);

        // 验证token
        $token = $data['token'] ?? '';
        if (empty($token)) {
            return Result::error(SeeLang::user('verify_passwd'));
        }

        if ($token !== See::cache()->get(md5(Auth::user()->uuid() . ''))) {
            return Result::error(SeeLang::user('verify_passwd_expired'));
        }

        $user = See::table()->user->find(Auth::user()->uuid());
        if (empty($user)) {
            return Result::error(SeeLang::user('not_found'));
        }

        $phone = $data['phone'];
        $phone = trim($phone);
        $sub = substr($phone, 3, 4);
        if ($sub == '****') {
            unset($data['phone']);
        }

        $data['id'] = Auth::user()->uuid();

        See::table()->user->save($data);

        $info = Auth::user()->info();
        $info = array_merge($info, $data);
        Auth::user()->info($info);

        return Result::success($data, Operate::saveSuccess());
    }

    /**
     * 用户状态
     * @return array
     */
    public function statusOptions(): array
    {
        return [
            0 => SeeLang::switch('forbid'),
            1 => SeeLang::switch('open')
        ];
    }


    /**
     * @param string $path_info
     * @return bool
     */
    public function checkPathPermission(string $path_info = ''): bool
    {
        if (Auth::user()->isRoot()) {
            return true;
        }

        if (!$path_info) {
            // nginx服务 PATH_INFO 为空
            $path_info = ($_SERVER['PATH_INFO'] ?? '') ?: ($_SERVER['REQUEST_URI'] ?? '');
        }

        if (empty($path_info)) {
            return false;
        }

        $path_info = str_replace('admin', '', $path_info);
        $path_info = trim($path_info, '/');

        $map_rule = function ($user_rule) use ($path_info) {
            $flag = false;
            foreach ($user_rule as $path) {
                // 将通配符 * 转换为正则表达式
                $pattern = '#^' . str_replace('\*', '[^/]*', preg_quote($path, '#')) . '$#';
                if (preg_match($pattern, $path_info)) {
                    $flag = true;
                    break;
                }
            }
            return $flag;
        };

        // 不需要权限访问权限的路由
        $public = [
            'home/*',
            'profile/*',
        ];
        $public = array_merge($public, See::config()->permission->expects());

        if ($map_rule($public)) {
            return true;
        }

        // 用户授权的规则列表
        try {
            $result = $map_rule($this->rule());
        } catch (SeeDbException $exception) {
            $result = false;
        }
        return $result;
    }
}